/**********************************************************************************************/
// dgt_PluginManager.cpp
//
// 
// Copyright Diamant Group 2007
/**********************************************************************************************/

#include "dgt_PluginManager.h"
#include "dgt_Plugin.h"

#include <wx/filename.h>
#include <wx/dir.h>
#include <wx/dynlib.h>
#include <wx/arrimpl.cpp>

WX_DEFINE_OBJARRAY( PluginArray );

/**********************************************************************************************/
// Function for loading all plugins and forming their list
//
/**********************************************************************************************/
void dgt_PluginManager::LoadAll( )
{
	// Getting name of our application
	wxChar buf[1024];
	*buf = '\0';
	GetModuleFileName(NULL, buf, 511);

	// Getting path to plugins directory
	wxString tPluginPath = get_Path();
	wxString tCompPath = get_CompPath();

	if( tPluginPath == wxEmptyString )	// Use Default
	{
		wxString path = wxPathOnly(buf);
		tPluginPath = path + ( wxFileName::GetPathSeparator() ) + PLUGIN_PATH;
	}
	if( tCompPath == wxEmptyString ) // Use Default
	{
		wxString path = wxPathOnly(buf);
		tCompPath = path + ( wxFileName::GetPathSeparator() ) + COMP_PATH;
	}

	if ( (!wxDirExists( tPluginPath ))  || (!wxDirExists( tCompPath )) )
		return;

	LoadDirectory( tPluginPath );
	LoadDirectory( tCompPath );
}

/**********************************************************************************************/
void dgt_PluginManager::LoadDirectory(wxString inPath)
{
	wxDir               t_PlugDir;
	bool                t_bResult = false;
	bool				tLoad = false;
	bool				t_bSuccess;
	wxString            t_strFileName;
	wxString  			t_strExtension = wxDynamicLibrary::GetDllExt();
	wxDynamicLibrary*   t_pPluginLib = NULL;
	wxString			t_strPluginPath;
	GETINSTANCEFUNC	    t_fpInstantiatePlugin;
	wxString            t_strInstantiateSymbol = _T("GetPlugin");
	dgt_Plugin*			t_pPlugin = NULL;

	static unsigned		t_num = 0;

	t_PlugDir.Open( inPath );
	if ( t_PlugDir.IsOpened() )
	{
		// get plugin file
		t_bResult = t_PlugDir.GetFirst( &t_strFileName, wxString( wxT("*") ) + t_strExtension );
		while( t_bResult )
		{
			t_pPluginLib = new wxDynamicLibrary();

			t_strPluginPath = inPath + wxString(wxFileName::GetPathSeparator()) + t_strFileName;

			// loading library
			tLoad = t_pPluginLib->Load( t_strPluginPath );

			if ( tLoad )
			{
				// finding GetPlugin function
				t_fpInstantiatePlugin = (GETINSTANCEFUNC)t_pPluginLib->GetSymbol( t_strInstantiateSymbol, &t_bSuccess );

				if( t_fpInstantiatePlugin && t_bSuccess )
				{
					// it is our plugin

					t_pPlugin = (*t_fpInstantiatePlugin)();

					Plugin tStruct;
					tStruct.m_LibName = t_strFileName;
					tStruct.m_lib = t_pPluginLib;
					tStruct.m_plugin.set( t_pPlugin );
					tStruct.m_Path = t_strPluginPath;

					m_Array.Add( tStruct );

					dgt_PluginInfo tInfo;
					t_pPlugin->GetPluginInfo( tInfo );
				
					// sorting plugins
					switch( tInfo.m_PluginType )
					{
						case PLUGIN_LOGIGAL:
						{
							m_Logical.Add( t_num );
							break;
						}
						case PLUGIN_OPERATION:
						{
							m_Operation.Add( t_num );
							break;
						}
						case PLUGIN_MEMORY:
						{
							m_Memory.Add( t_num );
							break;
						}
						case PLUGIN_CIRCUIT:
						{
							m_Circuit.Add( t_num );
							break;
						}
						case PLUGIN_TRIGGER:
						{
							m_Trigger.Add( t_num );
							break;
						}
						case PLUGIN_SYNTEZ_OA:
						{
							m_OASynt.Add( t_num );
							break;
						}
						case PLUGIN_SYNTEZ_CA:
						{
							m_CASynt.Add( t_num );
							break;
						}
						case PLUGIN_STORE:
						{
							m_Store.Add( t_num );
							break;
						}
						case PLUGIN_OUT:
						{
							m_Out.Add( t_num );
							break;
						}
						default:
							break;
					}

					t_num++;
				}
				else
				{
					// not DStudio plugin
					wxString ErrString = _T("Error loading ") + t_strFileName;
					wxMessageBox(ErrString, _T("ERROR"), wxOK|wxICON_INFORMATION );
					t_pPluginLib->Unload();
				}
			}
//			t_pPluginLib = NULL;
			t_bResult = t_PlugDir.GetNext( &t_strFileName );
		}
	}
}

/**********************************************************************************************/
// Function for getting number of plugins
//
unsigned dgt_PluginManager::get_PluginCount()
{
	return m_Array.Count();
}

/**********************************************************************************************/
// Function for getting number of plugins by type
//
unsigned dgt_PluginManager::get_PluginCount(PLUGIN_TYPE inType)
{
	switch( inType )
	{
		case PLUGIN_LOGIGAL:
		{
			return m_Logical.Count();
			break;
		}
		case PLUGIN_OPERATION:
		{
			return m_Operation.Count();
			break;
		}
		case PLUGIN_MEMORY:
		{
			return m_Memory.Count();
			break;
		}
		case PLUGIN_TRIGGER:
		{
			return m_Trigger.Count();
			break;
		}
		case PLUGIN_CIRCUIT:
		{
			return m_Circuit.Count();
			break;
		}
		case PLUGIN_SYNTEZ_OA:
		{
			return m_OASynt.Count();
			break;
		}
		case PLUGIN_SYNTEZ_CA:
		{
			return m_CASynt.Count();
			break;
		}
		case PLUGIN_STORE:
		{
			return m_Store.Count();
			break;
		}
		case PLUGIN_OUT:
		{
			return m_Out.Count();
			break;
		}
		case PLUGIN_COMP:
		{
			return m_Memory.Count() + m_Trigger.Count() + m_Operation.Count() + m_Logical.Count() + m_Circuit.Count();
		}
		case PLUGIN_SYNTEZ:
		{
			return m_CASynt.Count() + m_OASynt.Count();
		}
		default:
			return 0;
	}
}

/**********************************************************************************************/
// Function for getting plugin by number( from 0 ), If there are no such number - returns NULL
//
dgt_Plugin_Ptr dgt_PluginManager::get_Plugin(unsigned inNum)
{
	dgt_Plugin_Ptr result;

	if( inNum >= m_Array.Count() )
		result.set( NULL );
	else
		result = m_Array.Item( inNum ).m_plugin;

	return result;
}

/**********************************************************************************************/
// Function for getting plugin by number( from 0 ) and type inType
// If there are no such number - returns NULL
//
dgt_Plugin_Ptr dgt_PluginManager::get_Plugin(PLUGIN_TYPE inType, unsigned int inNum)
{
	dgt_Plugin_Ptr result;
	wxArrayInt tArray;

	switch( inType )
	{
		case PLUGIN_LOGIGAL:
		{
			tArray = m_Logical;
			break;
		}
		case PLUGIN_OPERATION:
		{
			tArray = m_Operation;
			break;
		}
		case PLUGIN_MEMORY:
		{
			tArray = m_Memory;
			break;
		}
		case PLUGIN_TRIGGER:
		{
			tArray = m_Trigger;
			break;
		}
		case PLUGIN_CIRCUIT:
		{
			tArray = m_Circuit;
			break;
		}
		case PLUGIN_SYNTEZ_OA:
		{
			tArray = m_OASynt;
			break;
		}
		case PLUGIN_SYNTEZ_CA:
		{
			tArray = m_CASynt;
			break;
		}
		case PLUGIN_STORE:
		{
			tArray = m_Store;
			break;
		}
		case PLUGIN_OUT:
		{
			tArray = m_Out;
			break;
		}
		case PLUGIN_SYNTEZ:
		{
			tArray = ArrCat( m_CASynt, m_OASynt );
			break;
		}
		case PLUGIN_COMP:
		{
			tArray = ArrCat( m_Memory, m_Trigger, m_Logical, m_Operation, m_Circuit );
		}
		default:
			result.set( NULL );
	}

	if( inNum >= tArray.Count() )
		result.set( NULL );
	else
	{
		int itemNum = tArray.Item( inNum );
		result = m_Array.Item( itemNum ).m_plugin;
	}

	return result;
}

/**********************************************************************************************/
// Returns number of plugin in manager by pointer to it. If there are no such plugin - returns -1
//
long dgt_PluginManager::get_Number(dgt_Plugin_Ptr inPlug)
{
	for ( unsigned long i = 0; i < m_Array.Count(); i++ )
	{
		dgt_Plugin_Ptr tPlug = m_Array.Item( i ).m_plugin; 
		if( inPlug.get() == tPlug.get() )
			return i; 
	}
	return -1;
}

/**********************************************************************************************/
// Load plugin by number inNum
//
void dgt_PluginManager::Load(unsigned inNum)
{
	wxDynamicLibrary *tLib = m_Array.Item( inNum ).m_lib;
	wxString tPath = m_Array.Item( inNum ).m_Path;

	if( !(tLib->IsLoaded()) )
		tLib->Load( tPath );
}

/**********************************************************************************************/
// Unload plugin by number inNum
//
void dgt_PluginManager::Unload(unsigned int inNum)
{
	wxDynamicLibrary *tLib = m_Array.Item( inNum ).m_lib; 

	if( tLib->IsLoaded() )
		tLib->Unload();
}

/**********************************************************************************************/
// Get TRUE if plugin by number inNum is loaded
//
bool dgt_PluginManager::IsLoaded(unsigned int inNum)
{
	wxDynamicLibrary *tLib = m_Array.Item( inNum ).m_lib; 

	return tLib->IsLoaded();
}

/**********************************************************************************************/
// Returns name of DLL of plugin
//
wxString dgt_PluginManager::get_PluginDllName( unsigned int inNum )
{
	return m_Array.Item( inNum ).m_LibName;
}

/**********************************************************************************************/
// Returns name of DLL of plugin with type inType and num inNum
//
wxString dgt_PluginManager::get_PluginDllName( PLUGIN_TYPE inType, unsigned int inNum )
{
	wxArrayInt tArray;

	switch( inType )
	{
		case PLUGIN_LOGIGAL:
		{
			tArray = m_Logical;
			break;
		}
		case PLUGIN_OPERATION:
		{
			tArray = m_Operation;
			break;
		}
		case PLUGIN_MEMORY:
		{
			tArray = m_Memory;
			break;
		}
		case PLUGIN_TRIGGER:
		{
			tArray = m_Trigger;
			break;
		}
		case PLUGIN_CIRCUIT:
		{
			tArray = m_Circuit;
			break;
		}
		case PLUGIN_SYNTEZ_OA:
		{
			tArray = m_OASynt;
			break;
		}
		case PLUGIN_SYNTEZ_CA:
		{
			tArray = m_CASynt;
			break;
		}
		case PLUGIN_STORE:
		{
			tArray = m_Store;
			break;
		}
		case PLUGIN_OUT:
		{
			tArray = m_Out;
			break;
		}
		case PLUGIN_SYNTEZ:
		{
			tArray = ArrCat( m_CASynt, m_OASynt );
			break;
		}
		case PLUGIN_COMP:
		{
			tArray = ArrCat( m_Memory, m_Trigger, m_Logical, m_Operation, m_Circuit );
		}
		default:
			return wxEmptyString;
	}

	if( inNum >= tArray.Count() )
		return wxEmptyString;
	else
	{
		int itemNum = tArray.Item( inNum );
		wxString result = m_Array.Item( itemNum ).m_LibName;
		return result;
	}
}

/**********************************************************************************************/
wxArrayInt dgt_PluginManager::ArrCat( wxArrayInt inArray1, wxArrayInt inArray2 )
{
	wxArrayInt result;
	result = inArray1;
	for( unsigned i = 0; i < inArray2.Count(); i++ )
		result.Add( inArray2.Item(i));

	return result;
}

/**********************************************************************************************/
wxArrayInt dgt_PluginManager::ArrCat(wxArrayInt inArray1, 
									 wxArrayInt inArray2, 
									 wxArrayInt inArray3, 
									 wxArrayInt inArray4,
									 wxArrayInt	inArray5 )
{
	wxArrayInt result, tres1, tres2, tres3;

	tres1 = ArrCat( inArray1, inArray2 );
	tres2 = ArrCat( inArray3, inArray4 );
	tres3 = ArrCat( tres2, inArray5 );
	result = ArrCat( tres1, tres3 );

	return result;
}

/**********************************************************************************************/
dgt_Plugin_Ptr dgt_PluginManager::get_PluginByLibName(wxString inName)
{
	dgt_Plugin_Ptr result;

	result.set( NULL );

	for( int i = 0; i < m_Array.Count(); i++ )
	{
		Plugin tPlug = m_Array.Item( i );
		wxFileName *tFilename = new wxFileName( tPlug.m_LibName );
		if( tFilename->GetFullPath() == inName )
		{
			result = tPlug.m_plugin;
			break;
		}
	}

	return result;
}

/**********************************************************************************************/
wxString dgt_PluginManager::get_PluginTypeString( PLUGIN_TYPE inType )
{
	switch( inType )
	{
		case PLUGIN_COMP:
		{
			return _("COMPONENTS");
		}
		case PLUGIN_LOGIGAL:
		{
			return _("COMPONENTS");
		}
		case PLUGIN_OPERATION:
		{
			return _("COMPONENTS");
		}
		case PLUGIN_MEMORY:
		{
			return _("COMPONENTS");
		}
		case PLUGIN_TRIGGER:
		{
			return _("COMPONENTS");
		}
		case PLUGIN_CIRCUIT:
		{
			return _("COMPONENTS");
		}
		case PLUGIN_SYNTEZ:
		{
			return _("SYNTEZ");
		}
		case PLUGIN_SYNTEZ_OA:
		{
			return _("SYNTEZ");
		}
		case PLUGIN_SYNTEZ_CA:
		{
			return _("SYNTEZ");
		}
		case PLUGIN_STORE:
		{
			return _("STORE");
		}
		case PLUGIN_OUT:
		{
			return _("OUTPUT");
		}
	}
}